﻿/********
* 
* 
*  Description:
*  Create By Kain at 4/2/2011 3:29:26 PM
*  
*
*  Revision History:
*  Date                  Who                 What
*  
* 
*/

#region

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Cherry.Agile.ServiceModel.Interceptor;
using System.ServiceModel.Description;

#endregion

namespace Cherry.Agile.ServiceModel.Runtime
{
    [Serializable]
    public class Service : IService, IDisposable
    {
        ServiceInfo _info;
        private Type _serviceType;
        private Type[] _contracts;
        private ServiceHost _host = null;

        public Service() {

        }

        public Service(int packageId, Type serviceType,string baseAddress = null)
        {
            _info = new ServiceInfo() { FullName = serviceType.FullName,
                                            Name = serviceType.Name,
                                            PackageId = packageId,
                                            BaseAddress = baseAddress
                                       };
            _serviceType = serviceType;
            var address = Config.RempServiceModelSection.Current.Service.Address;
            if (!string.IsNullOrEmpty(address))
                _info.BaseAddress = address;
            _contracts = FindContracts();
            System.Diagnostics.Debug.Assert(_contracts.Length > 0);
        }

        public ServiceInfo Info
        {
            get { return _info; }
        }

        public RunState RunState
        {
            get
            {
                if (_info.State == Runtime.RunState.Error)
                    return RunState.Error;
                if (_host == null)
                    return RunState.NotRunnable;

                if (_host.State == CommunicationState.Faulted)
                    return RunState.Error;
                else if (_host.State == CommunicationState.Opened)
                    return RunState.Runnable;
                else 
                    return Runtime.RunState.NotRunnable;
            }
        }

        public Type ServiceType
        {
            get { return _serviceType; }
        }

        public Type[] Contracts
        {
            get { return _contracts; }
        }

        public void Run()
        {
            if (_host == null)
            {
                _host = new ServiceHost(_serviceType);
                if (!Cherry.Agile.ServiceModel.Runtime.RuntimeUnity.ServiceHasConfig(ServiceType))
                    _host.ApplyEndpoints(this);
                LoadEndpointsInfomation();
            }
            try
            {
                if (_host.State != CommunicationState.Opening)
                    _host.Open();
            }
            catch
            {
                Info.State = Runtime.RunState.Error;
                _host.Abort();
                _host = null;
                throw;
            }
           
        }

        public void Stop()
        {
            if (_host != null)
                _host.Close();
        }

        public void Dispose()
        {
            if (_host != null && _host.State != CommunicationState.Closed)
                _host.Close();
        }

        private Type[] FindContracts()
        {
            return _serviceType.GetInterfaces().Where(c => Validate(c)).ToArray();
        }

        private bool Validate(Type type)
        {
            var atrs = type.GetCustomAttributes(typeof(ServiceContractAttribute), false);
            return atrs != null && atrs.Length > 0;
        }

        private void LoadEndpointsInfomation()
        {
           _info.EndpointsInfo = _host.Description.Endpoints.Select( c=>
            {
                EndpointInfo ei = new EndpointInfo();
                ei.ContractName = c.Contract.ContractType.FullName;
                ei.Binding = c.Binding.Name;
                ei.Address = c.Address.Uri.ToString();
                return ei;
            }).ToArray();
        }


        ServiceEndpoint[] _addresses;
        public ServiceEndpoint[] ServiceEndpointes {
            get {
                if (_addresses == null) {
                    _addresses = Contracts.Select(c => DefaultEndpointPolicy.EndpointPolicy.
                        CreateServiceEndpoint(c, _info.BaseAddress)).ToArray();
                }
                return _addresses;
            }
        }

        //void ApplyEndpoints(IService service, string baseAddress = null) {
        //    if (baseAddress == string.Empty)
        //        baseAddress = null;

        //    baseAddress = baseAddress ?? service.Info.BaseAddress;
        //    baseAddress = baseAddress ?? (service.Info.PackageId % 10000).ToString();

        //    foreach (var item in service.Contracts) {
        //        host.AddServiceEndpoint(__endpointPolicy.CreateEndpoint(item, baseAddress));
        //    }
        //}
    }
}
